pyOS-kernel 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pyos/kernel.py ADDED
@@ -0,0 +1,288 @@
1
+ """
2
+ pyOS Kernel - The core of your operating system
3
+ """
4
+
5
+ from typing import Callable, Optional, List, Dict, Any
6
+ from dataclasses import dataclass, field
7
+ from enum import Enum
8
+
9
+
10
+ class Architecture(Enum):
11
+ X86 = "x86"
12
+ X86_64 = "x86_64"
13
+
14
+
15
+ @dataclass
16
+ class KernelConfig:
17
+ """Configuration for the kernel"""
18
+ arch: Architecture = Architecture.X86
19
+ stack_size: int = 16384 # 16KB stack
20
+ heap_size: int = 1048576 # 1MB heap
21
+ video_mode: str = "text" # text or graphics
22
+ enable_interrupts: bool = True
23
+ enable_gdt: bool = True
24
+ enable_paging: bool = False
25
+
26
+
27
+ @dataclass
28
+ class KernelFunction:
29
+ """Represents a registered kernel function"""
30
+ name: str
31
+ func: Callable
32
+ event: str
33
+ priority: int = 0
34
+
35
+
36
+ class Kernel:
37
+ """
38
+ The main Kernel class for building operating systems.
39
+
40
+ Example:
41
+ kernel = Kernel(arch="x86")
42
+
43
+ @kernel.on_boot
44
+ def main():
45
+ Screen.print("Hello from MyOS!")
46
+
47
+ kernel.build("myos.iso")
48
+ """
49
+
50
+ def __init__(
51
+ self,
52
+ arch: str = "x86",
53
+ stack_size: int = 16384,
54
+ heap_size: int = 1048576,
55
+ enable_interrupts: bool = True,
56
+ enable_gdt: bool = True,
57
+ ):
58
+ """
59
+ Initialize a new Kernel.
60
+
61
+ Args:
62
+ arch: Target architecture ("x86" or "x86_64")
63
+ stack_size: Stack size in bytes (default 16KB)
64
+ heap_size: Heap size in bytes (default 1MB)
65
+ enable_interrupts: Enable interrupt handling
66
+ enable_gdt: Enable Global Descriptor Table
67
+ """
68
+ self.config = KernelConfig(
69
+ arch=Architecture(arch),
70
+ stack_size=stack_size,
71
+ heap_size=heap_size,
72
+ enable_interrupts=enable_interrupts,
73
+ enable_gdt=enable_gdt,
74
+ )
75
+
76
+ self._boot_functions: List[KernelFunction] = []
77
+ self._interrupt_handlers: Dict[int, KernelFunction] = {}
78
+ self._syscall_handlers: Dict[int, KernelFunction] = {}
79
+ self._keypress_handlers: List[KernelFunction] = []
80
+ self._timer_handlers: List[KernelFunction] = []
81
+ self._custom_handlers: Dict[str, List[KernelFunction]] = {}
82
+
83
+ self._compiled_asm: Optional[str] = None
84
+ self._compiled_binary: Optional[bytes] = None
85
+
86
+ def on_boot(self, func: Callable = None, *, priority: int = 0):
87
+ """
88
+ Decorator to register a function to run at boot.
89
+
90
+ Example:
91
+ @kernel.on_boot
92
+ def start():
93
+ Screen.print("Booting...")
94
+ """
95
+ def decorator(f: Callable) -> Callable:
96
+ self._boot_functions.append(KernelFunction(
97
+ name=f.__name__,
98
+ func=f,
99
+ event="boot",
100
+ priority=priority,
101
+ ))
102
+ # Sort by priority (higher first)
103
+ self._boot_functions.sort(key=lambda x: -x.priority)
104
+ return f
105
+
106
+ if func is not None:
107
+ return decorator(func)
108
+ return decorator
109
+
110
+ def on_keypress(self, func: Callable = None, *, key: str = None):
111
+ """
112
+ Decorator to handle keyboard input.
113
+
114
+ Example:
115
+ @kernel.on_keypress
116
+ def handle_key(key):
117
+ Screen.print(f"Pressed: {key}")
118
+ """
119
+ def decorator(f: Callable) -> Callable:
120
+ self._keypress_handlers.append(KernelFunction(
121
+ name=f.__name__,
122
+ func=f,
123
+ event="keypress",
124
+ ))
125
+ return f
126
+
127
+ if func is not None:
128
+ return decorator(func)
129
+ return decorator
130
+
131
+ def on_interrupt(self, interrupt_number: int):
132
+ """
133
+ Decorator to handle a specific interrupt.
134
+
135
+ Example:
136
+ @kernel.on_interrupt(0x21) # Keyboard interrupt
137
+ def keyboard_handler():
138
+ pass
139
+ """
140
+ def decorator(func: Callable) -> Callable:
141
+ self._interrupt_handlers[interrupt_number] = KernelFunction(
142
+ name=func.__name__,
143
+ func=func,
144
+ event=f"interrupt_{interrupt_number}",
145
+ )
146
+ return func
147
+ return decorator
148
+
149
+ def on_syscall(self, syscall_number: int):
150
+ """
151
+ Decorator to register a system call handler.
152
+
153
+ Example:
154
+ @kernel.on_syscall(1) # sys_write
155
+ def sys_write(fd, buf, count):
156
+ pass
157
+ """
158
+ def decorator(func: Callable) -> Callable:
159
+ self._syscall_handlers[syscall_number] = KernelFunction(
160
+ name=func.__name__,
161
+ func=func,
162
+ event=f"syscall_{syscall_number}",
163
+ )
164
+ return func
165
+ return decorator
166
+
167
+ def on_timer(self, interval_ms: int = 1000):
168
+ """
169
+ Decorator to register a timer handler.
170
+
171
+ Example:
172
+ @kernel.on_timer(interval_ms=1000)
173
+ def every_second():
174
+ pass
175
+ """
176
+ def decorator(func: Callable) -> Callable:
177
+ handler = KernelFunction(
178
+ name=func.__name__,
179
+ func=func,
180
+ event="timer",
181
+ )
182
+ handler.interval_ms = interval_ms
183
+ self._timer_handlers.append(handler)
184
+ return func
185
+ return decorator
186
+
187
+ def on_event(self, event_name: str):
188
+ """
189
+ Decorator to register a custom event handler.
190
+
191
+ Example:
192
+ @kernel.on_event("custom_event")
193
+ def handle_custom():
194
+ pass
195
+ """
196
+ def decorator(func: Callable) -> Callable:
197
+ if event_name not in self._custom_handlers:
198
+ self._custom_handlers[event_name] = []
199
+ self._custom_handlers[event_name].append(KernelFunction(
200
+ name=func.__name__,
201
+ func=func,
202
+ event=event_name,
203
+ ))
204
+ return func
205
+ return decorator
206
+
207
+ def compile(self) -> str:
208
+ """
209
+ Compile the kernel to Assembly code.
210
+
211
+ Returns:
212
+ The generated Assembly code as a string.
213
+ """
214
+ from .compiler.codegen import CodeGenerator
215
+
216
+ generator = CodeGenerator(self)
217
+ self._compiled_asm = generator.generate()
218
+ return self._compiled_asm
219
+
220
+ def assemble(self) -> bytes:
221
+ """
222
+ Assemble the kernel to machine code.
223
+
224
+ Returns:
225
+ The binary machine code.
226
+ """
227
+ if self._compiled_asm is None:
228
+ self.compile()
229
+
230
+ from .compiler.assembler import Assembler
231
+
232
+ assembler = Assembler(self.config.arch)
233
+ self._compiled_binary = assembler.assemble(self._compiled_asm)
234
+ return self._compiled_binary
235
+
236
+ def build(self, output: str, format: str = "iso") -> str:
237
+ """
238
+ Build the complete OS image.
239
+
240
+ Args:
241
+ output: Output file path
242
+ format: Output format ("iso" or "bin")
243
+
244
+ Returns:
245
+ Path to the generated file.
246
+ """
247
+ from .builder import OSBuilder
248
+
249
+ if self._compiled_binary is None:
250
+ self.assemble()
251
+
252
+ builder = OSBuilder(self)
253
+
254
+ if format == "iso":
255
+ return builder.build_iso(output)
256
+ elif format == "bin":
257
+ return builder.build_bin(output)
258
+ else:
259
+ raise ValueError(f"Unknown format: {format}")
260
+
261
+ def run(self, image_path: str = None, debug: bool = False):
262
+ """
263
+ Run the OS in QEMU.
264
+
265
+ Args:
266
+ image_path: Path to the OS image (builds if None)
267
+ debug: Enable QEMU debug mode
268
+ """
269
+ from .emulator import QEMURunner
270
+
271
+ if image_path is None:
272
+ image_path = self.build("temp_os.iso")
273
+
274
+ runner = QEMURunner(self.config.arch)
275
+ runner.run(image_path, debug=debug)
276
+
277
+ def get_info(self) -> Dict[str, Any]:
278
+ """Get information about the kernel configuration."""
279
+ return {
280
+ "architecture": self.config.arch.value,
281
+ "stack_size": self.config.stack_size,
282
+ "heap_size": self.config.heap_size,
283
+ "interrupts_enabled": self.config.enable_interrupts,
284
+ "gdt_enabled": self.config.enable_gdt,
285
+ "boot_functions": len(self._boot_functions),
286
+ "interrupt_handlers": len(self._interrupt_handlers),
287
+ "syscall_handlers": len(self._syscall_handlers),
288
+ }
@@ -0,0 +1,8 @@
1
+ """
2
+ pyOS Memory Management
3
+ """
4
+
5
+ from .manager import Memory
6
+ from .gdt import GDT
7
+
8
+ __all__ = ["Memory", "GDT"]
pyos/memory/gdt.py ADDED
@@ -0,0 +1,295 @@
1
+ """
2
+ pyOS GDT (Global Descriptor Table) Management
3
+ """
4
+
5
+ from typing import List, Optional
6
+ from dataclasses import dataclass
7
+ from enum import Enum, Flag
8
+
9
+
10
+ class SegmentType(Enum):
11
+ """Segment types for GDT entries"""
12
+ NULL = 0
13
+ CODE_EXECUTE_ONLY = 0x8
14
+ CODE_EXECUTE_READ = 0xA
15
+ CODE_CONFORMING_EXECUTE_ONLY = 0xC
16
+ CODE_CONFORMING_EXECUTE_READ = 0xE
17
+ DATA_READ_ONLY = 0x0
18
+ DATA_READ_WRITE = 0x2
19
+ DATA_EXPAND_DOWN_READ_ONLY = 0x4
20
+ DATA_EXPAND_DOWN_READ_WRITE = 0x6
21
+
22
+
23
+ class PrivilegeLevel(Enum):
24
+ """CPU privilege levels (rings)"""
25
+ RING0 = 0 # Kernel mode
26
+ RING1 = 1
27
+ RING2 = 2
28
+ RING3 = 3 # User mode
29
+
30
+
31
+ @dataclass
32
+ class GDTEntry:
33
+ """
34
+ Represents a single GDT entry.
35
+
36
+ Attributes:
37
+ base: Base address of the segment
38
+ limit: Size of the segment
39
+ segment_type: Type of segment (code/data)
40
+ privilege: Privilege level (ring)
41
+ present: Is segment present in memory
42
+ granularity: Limit granularity (byte or 4KB)
43
+ is_32bit: 32-bit or 16-bit segment
44
+ """
45
+ base: int = 0
46
+ limit: int = 0xFFFFF
47
+ segment_type: SegmentType = SegmentType.DATA_READ_WRITE
48
+ privilege: PrivilegeLevel = PrivilegeLevel.RING0
49
+ present: bool = True
50
+ granularity: bool = True # True = 4KB, False = 1 byte
51
+ is_32bit: bool = True
52
+
53
+ def to_bytes(self) -> bytes:
54
+ """Convert GDT entry to 8-byte descriptor."""
55
+ # Limit (bits 0-15)
56
+ limit_low = self.limit & 0xFFFF
57
+ # Base (bits 0-15)
58
+ base_low = self.base & 0xFFFF
59
+ # Base (bits 16-23)
60
+ base_mid = (self.base >> 16) & 0xFF
61
+
62
+ # Access byte
63
+ access = 0
64
+ if self.present:
65
+ access |= 0x80 # Present bit
66
+ access |= (self.privilege.value << 5) # DPL
67
+ access |= 0x10 # Descriptor type (1 = code/data)
68
+ access |= self.segment_type.value
69
+
70
+ # Flags + Limit (bits 16-19)
71
+ flags_limit = (self.limit >> 16) & 0x0F
72
+ if self.granularity:
73
+ flags_limit |= 0x80 # Granularity
74
+ if self.is_32bit:
75
+ flags_limit |= 0x40 # 32-bit
76
+
77
+ # Base (bits 24-31)
78
+ base_high = (self.base >> 24) & 0xFF
79
+
80
+ return bytes([
81
+ limit_low & 0xFF,
82
+ (limit_low >> 8) & 0xFF,
83
+ base_low & 0xFF,
84
+ (base_low >> 8) & 0xFF,
85
+ base_mid,
86
+ access,
87
+ flags_limit,
88
+ base_high,
89
+ ])
90
+
91
+
92
+ class GDT:
93
+ """
94
+ Global Descriptor Table Manager.
95
+
96
+ The GDT defines memory segments for the CPU.
97
+
98
+ Example:
99
+ gdt = GDT()
100
+ gdt.add_null_descriptor()
101
+ gdt.add_code_segment(base=0, limit=0xFFFFF, ring=0)
102
+ gdt.add_data_segment(base=0, limit=0xFFFFF, ring=0)
103
+ gdt.install()
104
+ """
105
+
106
+ _entries: List[GDTEntry] = []
107
+ _operations: list = []
108
+
109
+ def __init__(self):
110
+ """Initialize GDT with empty entries."""
111
+ self._entries = []
112
+ self._operations = []
113
+
114
+ def add_null_descriptor(self) -> 'GDT':
115
+ """
116
+ Add the required null descriptor (first entry).
117
+
118
+ Returns:
119
+ Self for chaining.
120
+
121
+ Example:
122
+ gdt.add_null_descriptor()
123
+ """
124
+ self._entries.append(GDTEntry(
125
+ base=0,
126
+ limit=0,
127
+ segment_type=SegmentType.NULL,
128
+ present=False,
129
+ ))
130
+ self._operations.append({"type": "add_null"})
131
+ return self
132
+
133
+ def add_code_segment(
134
+ self,
135
+ base: int = 0,
136
+ limit: int = 0xFFFFF,
137
+ ring: int = 0,
138
+ readable: bool = True,
139
+ ) -> 'GDT':
140
+ """
141
+ Add a code segment descriptor.
142
+
143
+ Args:
144
+ base: Base address (default: 0)
145
+ limit: Segment limit (default: 4GB)
146
+ ring: Privilege level 0-3 (default: 0 = kernel)
147
+ readable: Allow reading code (default: True)
148
+
149
+ Returns:
150
+ Self for chaining.
151
+
152
+ Example:
153
+ gdt.add_code_segment(ring=0) # Kernel code
154
+ gdt.add_code_segment(ring=3) # User code
155
+ """
156
+ seg_type = SegmentType.CODE_EXECUTE_READ if readable else SegmentType.CODE_EXECUTE_ONLY
157
+
158
+ self._entries.append(GDTEntry(
159
+ base=base,
160
+ limit=limit,
161
+ segment_type=seg_type,
162
+ privilege=PrivilegeLevel(ring),
163
+ ))
164
+ self._operations.append({
165
+ "type": "add_code",
166
+ "base": base,
167
+ "limit": limit,
168
+ "ring": ring,
169
+ })
170
+ return self
171
+
172
+ def add_data_segment(
173
+ self,
174
+ base: int = 0,
175
+ limit: int = 0xFFFFF,
176
+ ring: int = 0,
177
+ writable: bool = True,
178
+ ) -> 'GDT':
179
+ """
180
+ Add a data segment descriptor.
181
+
182
+ Args:
183
+ base: Base address (default: 0)
184
+ limit: Segment limit (default: 4GB)
185
+ ring: Privilege level 0-3 (default: 0 = kernel)
186
+ writable: Allow writing (default: True)
187
+
188
+ Returns:
189
+ Self for chaining.
190
+
191
+ Example:
192
+ gdt.add_data_segment(ring=0) # Kernel data
193
+ gdt.add_data_segment(ring=3) # User data
194
+ """
195
+ seg_type = SegmentType.DATA_READ_WRITE if writable else SegmentType.DATA_READ_ONLY
196
+
197
+ self._entries.append(GDTEntry(
198
+ base=base,
199
+ limit=limit,
200
+ segment_type=seg_type,
201
+ privilege=PrivilegeLevel(ring),
202
+ ))
203
+ self._operations.append({
204
+ "type": "add_data",
205
+ "base": base,
206
+ "limit": limit,
207
+ "ring": ring,
208
+ })
209
+ return self
210
+
211
+ def add_tss_segment(self, base: int, limit: int) -> 'GDT':
212
+ """
213
+ Add a Task State Segment descriptor.
214
+
215
+ Args:
216
+ base: TSS base address
217
+ limit: TSS size
218
+
219
+ Returns:
220
+ Self for chaining.
221
+ """
222
+ # TSS descriptor has special format
223
+ self._entries.append(GDTEntry(
224
+ base=base,
225
+ limit=limit,
226
+ segment_type=SegmentType.CODE_EXECUTE_ONLY, # Will be modified
227
+ privilege=PrivilegeLevel.RING0,
228
+ ))
229
+ self._operations.append({
230
+ "type": "add_tss",
231
+ "base": base,
232
+ "limit": limit,
233
+ })
234
+ return self
235
+
236
+ def install(self) -> None:
237
+ """
238
+ Install the GDT and reload segment registers.
239
+
240
+ This must be called after adding all segments.
241
+
242
+ Example:
243
+ gdt.install()
244
+ """
245
+ self._operations.append({"type": "install"})
246
+
247
+ def get_selector(self, index: int, ring: int = 0) -> int:
248
+ """
249
+ Get the segment selector for an entry.
250
+
251
+ Args:
252
+ index: GDT entry index
253
+ ring: Requested privilege level
254
+
255
+ Returns:
256
+ The segment selector value.
257
+ """
258
+ return (index << 3) | ring
259
+
260
+ def to_bytes(self) -> bytes:
261
+ """Convert entire GDT to bytes."""
262
+ result = b''
263
+ for entry in self._entries:
264
+ result += entry.to_bytes()
265
+ return result
266
+
267
+ def get_size(self) -> int:
268
+ """Get GDT size in bytes."""
269
+ return len(self._entries) * 8
270
+
271
+ def _get_operations(self) -> list:
272
+ """Get all recorded operations (used by compiler)."""
273
+ return self._operations.copy()
274
+
275
+ @classmethod
276
+ def create_flat_model(cls) -> 'GDT':
277
+ """
278
+ Create a standard flat memory model GDT.
279
+
280
+ This is the most common setup for modern OS.
281
+
282
+ Returns:
283
+ Configured GDT instance.
284
+
285
+ Example:
286
+ gdt = GDT.create_flat_model()
287
+ gdt.install()
288
+ """
289
+ gdt = cls()
290
+ gdt.add_null_descriptor()
291
+ gdt.add_code_segment(ring=0) # Kernel code
292
+ gdt.add_data_segment(ring=0) # Kernel data
293
+ gdt.add_code_segment(ring=3) # User code
294
+ gdt.add_data_segment(ring=3) # User data
295
+ return gdt