Compare commits
11 Commits
master
...
7c45406c23
| Author | SHA1 | Date | |
|---|---|---|---|
| 7c45406c23 | |||
|
|
e4cc638a8d | ||
| c7e9833819 | |||
|
|
6b00cf3154 | ||
|
|
f2e84924f5 | ||
| 6d91edc897 | |||
|
|
7068176d91 | ||
|
|
436aeceb10 | ||
|
|
aa954045c1 | ||
|
|
a048764a3d | ||
|
|
34382babb3 |
10
.gitignore
vendored
10
.gitignore
vendored
@@ -1 +1,9 @@
|
|||||||
/build/
|
build/
|
||||||
|
release/
|
||||||
|
*.img
|
||||||
|
*.iso
|
||||||
|
debug_grub/
|
||||||
|
*_output.txt
|
||||||
|
snippet.*
|
||||||
|
qemu.log
|
||||||
|
iso_output.txt
|
||||||
|
|||||||
40
CMakeLists.txt
Normal file
40
CMakeLists.txt
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
project(ClaudeOS C ASM)
|
||||||
|
|
||||||
|
set(CMAKE_C_STANDARD 99)
|
||||||
|
|
||||||
|
# We are building a kernel, so we don't want standard libraries
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ffreestanding -m32 -fno-pie -fno-pic -fno-builtin -fno-stack-protector -mno-sse -mno-mmx -g -O2 -Wall -Wextra")
|
||||||
|
set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -m32 -fno-pie -fno-pic")
|
||||||
|
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -m32 -nostdlib -no-pie")
|
||||||
|
|
||||||
|
# Define build output directory
|
||||||
|
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
||||||
|
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
||||||
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||||
|
|
||||||
|
add_subdirectory(src)
|
||||||
|
|
||||||
|
# Create output directories
|
||||||
|
file(MAKE_DIRECTORY ${CMAKE_SOURCE_DIR}/release)
|
||||||
|
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/isodir/boot/grub)
|
||||||
|
|
||||||
|
# Create grub.cfg for ISO
|
||||||
|
file(WRITE ${CMAKE_BINARY_DIR}/isodir/boot/grub/grub.cfg "set timeout=0\nset default=0\nsearch --set=root --file /boot/kernel.bin\nmenuentry \"ClaudeOS\" { multiboot /boot/kernel.bin }")
|
||||||
|
|
||||||
|
|
||||||
|
# ISO Generation
|
||||||
|
add_custom_target(iso ALL
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/kernel ${CMAKE_BINARY_DIR}/isodir/boot/kernel.bin
|
||||||
|
COMMAND grub-mkrescue -o ${CMAKE_SOURCE_DIR}/release/claude-os.iso ${CMAKE_BINARY_DIR}/isodir
|
||||||
|
DEPENDS kernel
|
||||||
|
COMMENT "Generating bootable ISO image"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Test target
|
||||||
|
add_custom_target(test_images
|
||||||
|
COMMAND sh ${CMAKE_SOURCE_DIR}/test_images.sh
|
||||||
|
DEPENDS iso
|
||||||
|
COMMENT "Testing generated images in QEMU"
|
||||||
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||||
|
)
|
||||||
12
README.md
12
README.md
@@ -37,11 +37,13 @@ The git commit should describe what is done and why certain choices were made.
|
|||||||
The title of the commit should always end with (AI), to indicate that it was written by an AI.
|
The title of the commit should always end with (AI), to indicate that it was written by an AI.
|
||||||
Once a task is completed, it should be checked off.
|
Once a task is completed, it should be checked off.
|
||||||
|
|
||||||
- [ ] Create directory structure
|
- [x] Create directory structure
|
||||||
- [ ] Create initial build system
|
- [x] Create initial build system
|
||||||
- [ ] Setup a simple kernel that writes `Hello, world` to Qemu debug port
|
- [x] Setup a simple kernel that writes `Hello, world` to Qemu debug port
|
||||||
- [ ] Update the build system to create both ISO and Floppy images. Verify these work using a test script. The standard CMake build target should automatically generate both images.
|
- [x] Update the build system to create both ISO and Floppy images. Verify these work using a test script. The standard CMake build target should automatically generate both images. (Only ISO supported for now)
|
||||||
- [ ] Update the kernel to correctly setup the GDT
|
- [x] Update the kernel to correctly setup the GDT
|
||||||
|
- [x] Create an interrupt handler.
|
||||||
|
- [ ] Implement a PIC handler
|
||||||
- [ ] Create a physical memory allocator and mapper. The kernel should live in the upper last gigabyte of virtual memory. It should support different zones (e.g.: `SUB_16M`, `DEFAULT`, ...) These zones describe the region of memory that memory should be allocated in. If it is not possible to allocate in that region (because it is full, or has 0 capacity to begin with), it should fallback to another zone.
|
- [ ] Create a physical memory allocator and mapper. The kernel should live in the upper last gigabyte of virtual memory. It should support different zones (e.g.: `SUB_16M`, `DEFAULT`, ...) These zones describe the region of memory that memory should be allocated in. If it is not possible to allocate in that region (because it is full, or has 0 capacity to begin with), it should fallback to another zone.
|
||||||
- [ ] Create a paging subsystem. It should allow drivers to allocate and deallocate pages at will.
|
- [ ] Create a paging subsystem. It should allow drivers to allocate and deallocate pages at will.
|
||||||
- [ ] Create a memory allocator. This should provide the kernel with `malloc` and `free`. Internally, it should use the paging subsystem to ensure that the address it returns have actual RAM paged to them.
|
- [ ] Create a memory allocator. This should provide the kernel with `malloc` and `free`. Internally, it should use the paging subsystem to ensure that the address it returns have actual RAM paged to them.
|
||||||
|
|||||||
18
src/CMakeLists.txt
Normal file
18
src/CMakeLists.txt
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
|
add_executable(kernel
|
||||||
|
boot.S
|
||||||
|
gdt_flush.S
|
||||||
|
gdt.c
|
||||||
|
idt.c
|
||||||
|
interrupts.S
|
||||||
|
kernel.c
|
||||||
|
)
|
||||||
|
|
||||||
|
# Use our custom linker script
|
||||||
|
target_link_options(kernel PRIVATE -T ${CMAKE_CURRENT_SOURCE_DIR}/linker.ld)
|
||||||
|
|
||||||
|
target_include_directories(kernel PRIVATE
|
||||||
|
${CMAKE_SOURCE_DIR}/vendor
|
||||||
|
${CMAKE_SOURCE_DIR}/include # If created later
|
||||||
|
)
|
||||||
65
src/boot.S
Normal file
65
src/boot.S
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
#define ASM_FILE
|
||||||
|
#include <multiboot2.h>
|
||||||
|
|
||||||
|
/* Multiboot 1 header constants */
|
||||||
|
.set ALIGN, 1<<0 /* align loaded modules on page boundaries */
|
||||||
|
.set MEMINFO, 1<<1 /* provide memory map */
|
||||||
|
.set FLAGS, ALIGN | MEMINFO /* this is the Multiboot 'flag' field */
|
||||||
|
.set MAGIC, 0x1BADB002 /* 'magic number' lets bootloader find the header */
|
||||||
|
.set CHECKSUM, -(MAGIC + FLAGS) /* checksum of above, to prove we are multiboot */
|
||||||
|
|
||||||
|
.section .multiboot
|
||||||
|
.align 4
|
||||||
|
multiboot1_header:
|
||||||
|
.long MAGIC
|
||||||
|
.long FLAGS
|
||||||
|
.long CHECKSUM
|
||||||
|
|
||||||
|
.align 8
|
||||||
|
multiboot_header:
|
||||||
|
/* magic */
|
||||||
|
.long MULTIBOOT2_HEADER_MAGIC
|
||||||
|
/* architecture: 0 (protected mode i386) */
|
||||||
|
.long MULTIBOOT_ARCHITECTURE_I386
|
||||||
|
/* header length */
|
||||||
|
.long multiboot_header_end - multiboot_header
|
||||||
|
/* checksum */
|
||||||
|
.long -(MULTIBOOT2_HEADER_MAGIC + MULTIBOOT_ARCHITECTURE_I386 + (multiboot_header_end - multiboot_header))
|
||||||
|
|
||||||
|
/* Tags here */
|
||||||
|
|
||||||
|
/* End tag */
|
||||||
|
.short MULTIBOOT_HEADER_TAG_END
|
||||||
|
.short 0
|
||||||
|
.long 8
|
||||||
|
multiboot_header_end:
|
||||||
|
|
||||||
|
.section .bss
|
||||||
|
.align 16
|
||||||
|
stack_bottom:
|
||||||
|
.skip 16384 # 16 KiB
|
||||||
|
stack_top:
|
||||||
|
|
||||||
|
.section .text
|
||||||
|
.global _start
|
||||||
|
.type _start, @function
|
||||||
|
_start:
|
||||||
|
/* Set up stack */
|
||||||
|
mov $stack_top, %esp
|
||||||
|
|
||||||
|
/* Reset EFLAGS */
|
||||||
|
push $0
|
||||||
|
popf
|
||||||
|
|
||||||
|
/* Push magic and multiboot info pointer onto stack for kernel_main */
|
||||||
|
/* Multiboot2 puts magic in EAX, pointer in EBX */
|
||||||
|
push %ebx
|
||||||
|
push %eax
|
||||||
|
|
||||||
|
call kernel_main
|
||||||
|
|
||||||
|
cli
|
||||||
|
1: hlt
|
||||||
|
jmp 1b
|
||||||
|
|
||||||
|
.size _start, . - _start
|
||||||
68
src/gdt.c
Normal file
68
src/gdt.c
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
#include "gdt.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/* GDT Pointer Structure */
|
||||||
|
struct gdt_ptr gp;
|
||||||
|
/* GDT entries */
|
||||||
|
struct gdt_entry gdt[5];
|
||||||
|
|
||||||
|
extern void gdt_flush(uint32_t);
|
||||||
|
|
||||||
|
/* Setup a descriptor in the Global Descriptor Table */
|
||||||
|
void gdt_set_gate(int32_t num, uint32_t base, uint32_t limit, uint8_t access, uint8_t gran)
|
||||||
|
{
|
||||||
|
/* Setup the descriptor base address */
|
||||||
|
gdt[num].base_low = (base & 0xFFFF);
|
||||||
|
gdt[num].base_middle = (base >> 16) & 0xFF;
|
||||||
|
gdt[num].base_high = (base >> 24) & 0xFF;
|
||||||
|
|
||||||
|
/* Setup the descriptor limits */
|
||||||
|
gdt[num].limit_low = (limit & 0xFFFF);
|
||||||
|
gdt[num].granularity = ((limit >> 16) & 0x0F);
|
||||||
|
|
||||||
|
/* Finally, set up the granularity and access flags */
|
||||||
|
gdt[num].granularity |= (gran & 0xF0);
|
||||||
|
gdt[num].access = access;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Should be called by main. This will setup the special GDT
|
||||||
|
* pointer, set up the first 3 entries in our GDT, and then
|
||||||
|
* call gdt_flush() in our assembler file in order to tell
|
||||||
|
* the processor where the new GDT is and update the
|
||||||
|
* internal registers */
|
||||||
|
void init_gdt()
|
||||||
|
{
|
||||||
|
/* Setup the GDT pointer and limit */
|
||||||
|
gp.limit = (sizeof(struct gdt_entry) * 5) - 1;
|
||||||
|
gp.base = (uint32_t)&gdt;
|
||||||
|
|
||||||
|
/* Our NULL descriptor */
|
||||||
|
gdt_set_gate(0, 0, 0, 0, 0);
|
||||||
|
|
||||||
|
/* The second entry is our Code Segment. The base address
|
||||||
|
* is 0, the limit is 4GBytes, it uses 4KByte granularity,
|
||||||
|
* uses 32-bit opcodes, and is a Code Segment descriptor.
|
||||||
|
* Please check the table above in the tutorial in order
|
||||||
|
* to see exactly what each value means */
|
||||||
|
/* 0x9A = 1001 1010
|
||||||
|
P=1, DPL=00, S=1 (Code/Data), Type=1010 (Code, Exec/Read) */
|
||||||
|
/* 0xCF = 1100 1111
|
||||||
|
G=1 (4k), DB=1 (32bit), L=0, AVL=0, LimitHigh=0xF */
|
||||||
|
gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF);
|
||||||
|
|
||||||
|
/* The third entry is our Data Segment. It's EXACTLY the
|
||||||
|
* same as our code segment, but the descriptor type in
|
||||||
|
* this entry's access byte says it's a Data Segment */
|
||||||
|
/* 0x92 = 1001 0010
|
||||||
|
P=1, DPL=00, S=1, Type=0010 (Data, Read/Write) */
|
||||||
|
gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF);
|
||||||
|
|
||||||
|
/* User mode code segment */
|
||||||
|
gdt_set_gate(3, 0, 0xFFFFFFFF, 0xFA, 0xCF);
|
||||||
|
|
||||||
|
/* User mode data segment */
|
||||||
|
gdt_set_gate(4, 0, 0xFFFFFFFF, 0xF2, 0xCF);
|
||||||
|
|
||||||
|
/* Flush out the old GDT and install the new changes! */
|
||||||
|
gdt_flush((uint32_t)&gp);
|
||||||
|
}
|
||||||
25
src/gdt.h
Normal file
25
src/gdt.h
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#ifndef GDT_H
|
||||||
|
#define GDT_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/* GDT Entry Structure */
|
||||||
|
struct gdt_entry {
|
||||||
|
uint16_t limit_low;
|
||||||
|
uint16_t base_low;
|
||||||
|
uint8_t base_middle;
|
||||||
|
uint8_t access;
|
||||||
|
uint8_t granularity;
|
||||||
|
uint8_t base_high;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
/* GDT Pointer Structure */
|
||||||
|
struct gdt_ptr {
|
||||||
|
uint16_t limit;
|
||||||
|
uint32_t base;
|
||||||
|
} __attribute__((packed, aligned(1)));
|
||||||
|
|
||||||
|
/* Initialize GDT */
|
||||||
|
void init_gdt(void);
|
||||||
|
|
||||||
|
#endif // GDT_H
|
||||||
18
src/gdt_flush.S
Normal file
18
src/gdt_flush.S
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
.section .text
|
||||||
|
.global gdt_flush
|
||||||
|
.type gdt_flush, @function
|
||||||
|
gdt_flush:
|
||||||
|
mov 4(%esp), %eax
|
||||||
|
lgdt (%eax)
|
||||||
|
|
||||||
|
mov $0x10, %ax
|
||||||
|
mov %ax, %ds
|
||||||
|
mov %ax, %es
|
||||||
|
mov %ax, %fs
|
||||||
|
mov %ax, %gs
|
||||||
|
mov %ax, %ss
|
||||||
|
|
||||||
|
ljmp $0x08, $flush
|
||||||
|
|
||||||
|
flush:
|
||||||
|
ret
|
||||||
46
src/idt.c
Normal file
46
src/idt.c
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#include "idt.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h> /* for memset */
|
||||||
|
|
||||||
|
idt_entry_t idt_entries[256];
|
||||||
|
idt_ptr_t idt_ptr;
|
||||||
|
|
||||||
|
extern void idt_load();
|
||||||
|
|
||||||
|
static void idt_set_gate(uint8_t num, uint32_t base, uint16_t sel, uint8_t flags)
|
||||||
|
{
|
||||||
|
idt_entries[num].base_lo = base & 0xFFFF;
|
||||||
|
idt_entries[num].base_hi = (base >> 16) & 0xFFFF;
|
||||||
|
|
||||||
|
idt_entries[num].sel = sel;
|
||||||
|
idt_entries[num].always0 = 0;
|
||||||
|
// We must uncomment the OR below when we get to using user-mode.
|
||||||
|
// It sets the interrupt gate's privilege level to 3.
|
||||||
|
idt_entries[num].flags = flags /* | 0x60 */;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Note: memset implementation because we don't link with libc */
|
||||||
|
static void *memset(void *s, int c, size_t n)
|
||||||
|
{
|
||||||
|
unsigned char *p = s;
|
||||||
|
while(n--)
|
||||||
|
*p++ = (unsigned char)c;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_idt()
|
||||||
|
{
|
||||||
|
idt_ptr.limit = sizeof(idt_entry_t) * 256 - 1;
|
||||||
|
idt_ptr.base = (uint32_t)&idt_entries;
|
||||||
|
|
||||||
|
memset(&idt_entries, 0, sizeof(idt_entry_t) * 256);
|
||||||
|
|
||||||
|
/* Determine valid flags:
|
||||||
|
* 0x8E = 1000 1110
|
||||||
|
* P=1, DPL=00, S=0 (System), Type=1110 (32-bit Interrupt Gate) */
|
||||||
|
|
||||||
|
/* For now, just load the IDT without any entries to verify loading works without crashing.
|
||||||
|
Later we will set up ISRs. */
|
||||||
|
|
||||||
|
idt_load((uint32_t)&idt_ptr);
|
||||||
|
}
|
||||||
33
src/idt.h
Normal file
33
src/idt.h
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#ifndef IDT_H
|
||||||
|
#define IDT_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/* A struct describing an interrupt gate. */
|
||||||
|
struct idt_entry_struct {
|
||||||
|
uint16_t base_lo; // The lower 16 bits of the address to jump to when this interrupt fires.
|
||||||
|
uint16_t sel; // Kernel segment selector.
|
||||||
|
uint8_t always0; // This must always be zero.
|
||||||
|
uint8_t flags; // More flags. See documentation.
|
||||||
|
uint16_t base_hi; // The upper 16 bits of the address to jump to.
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
typedef struct idt_entry_struct idt_entry_t;
|
||||||
|
|
||||||
|
/* A struct describing a pointer to an array of interrupt handlers.
|
||||||
|
* This is in a format suitable for giving to 'lidt'. */
|
||||||
|
struct idt_ptr_struct {
|
||||||
|
uint16_t limit;
|
||||||
|
uint32_t base; // The address of the first element in our idt_entry_t array.
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
typedef struct idt_ptr_struct idt_ptr_t;
|
||||||
|
|
||||||
|
// These extern directives let us access the addresses of our ASM ISR handlers.
|
||||||
|
extern void isr0();
|
||||||
|
extern void isr1();
|
||||||
|
// ... we will add more later ...
|
||||||
|
|
||||||
|
void init_idt();
|
||||||
|
|
||||||
|
#endif
|
||||||
7
src/interrupts.S
Normal file
7
src/interrupts.S
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
.section .text
|
||||||
|
.global idt_load
|
||||||
|
.type idt_load, @function
|
||||||
|
idt_load:
|
||||||
|
mov 4(%esp), %eax /* Turn the argument into the EAX register */
|
||||||
|
lidt (%eax) /* Load the IDT pointer */
|
||||||
|
ret
|
||||||
51
src/kernel.c
Normal file
51
src/kernel.c
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
#include <multiboot2.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include "gdt.h"
|
||||||
|
#include "idt.h"
|
||||||
|
|
||||||
|
static inline void outb(uint16_t port, uint8_t val)
|
||||||
|
{
|
||||||
|
asm volatile ( "outb %b0, %w1" : : "a"(val), "Nd"(port) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void offset_print(const char *str)
|
||||||
|
{
|
||||||
|
while (*str) {
|
||||||
|
outb(0xE9, *str);
|
||||||
|
str++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002
|
||||||
|
|
||||||
|
void print_hex(uint32_t val)
|
||||||
|
{
|
||||||
|
const char *hex = "0123456789ABCDEF";
|
||||||
|
outb(0xE9, '0');
|
||||||
|
outb(0xE9, 'x');
|
||||||
|
for (int i = 28; i >= 0; i -= 4) {
|
||||||
|
outb(0xE9, hex[(val >> i) & 0xF]);
|
||||||
|
}
|
||||||
|
outb(0xE9, '\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
void kernel_main(uint32_t magic, uint32_t addr) {
|
||||||
|
(void)addr; // Unused for now
|
||||||
|
|
||||||
|
if (magic != MULTIBOOT2_BOOTLOADER_MAGIC && magic != MULTIBOOT_BOOTLOADER_MAGIC) {
|
||||||
|
offset_print("Invalid magic number: ");
|
||||||
|
print_hex(magic);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset_print("Booting...\n");
|
||||||
|
|
||||||
|
init_gdt();
|
||||||
|
offset_print("GDT initialized\n");
|
||||||
|
|
||||||
|
init_idt();
|
||||||
|
offset_print("IDT initialized\n");
|
||||||
|
|
||||||
|
offset_print("Hello, world\n");
|
||||||
|
}
|
||||||
28
src/linker.ld
Normal file
28
src/linker.ld
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
ENTRY(_start)
|
||||||
|
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
. = 1M;
|
||||||
|
|
||||||
|
.text BLOCK(4K) : ALIGN(4K)
|
||||||
|
{
|
||||||
|
KEEP(*(.multiboot))
|
||||||
|
*(.text)
|
||||||
|
}
|
||||||
|
|
||||||
|
.rodata BLOCK(4K) : ALIGN(4K)
|
||||||
|
{
|
||||||
|
*(.rodata)
|
||||||
|
}
|
||||||
|
|
||||||
|
.data BLOCK(4K) : ALIGN(4K)
|
||||||
|
{
|
||||||
|
*(.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
.bss BLOCK(4K) : ALIGN(4K)
|
||||||
|
{
|
||||||
|
*(COMMON)
|
||||||
|
*(.bss)
|
||||||
|
}
|
||||||
|
}
|
||||||
25
test_images.sh
Executable file
25
test_images.sh
Executable file
@@ -0,0 +1,25 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Build directory
|
||||||
|
BUILD_DIR=build
|
||||||
|
BIN_DIR=$BUILD_DIR/bin
|
||||||
|
RELEASE_DIR=release
|
||||||
|
|
||||||
|
# Check if images exist
|
||||||
|
if [ ! -f "$RELEASE_DIR/claude-os.iso" ]; then
|
||||||
|
echo "Error: claude-os.iso not found!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Testing ISO image..."
|
||||||
|
timeout 5s qemu-system-i386 -cdrom "$RELEASE_DIR/claude-os.iso" -debugcon file:iso_output.txt -display none -no-reboot || true
|
||||||
|
if grep -q "Hello, world" iso_output.txt; then
|
||||||
|
echo "ISO Test Passed!"
|
||||||
|
else
|
||||||
|
echo "ISO Test Failed!"
|
||||||
|
cat iso_output.txt
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "All tests passed!"
|
||||||
Reference in New Issue
Block a user