독서정리 - OS in 1000 lines
Contents
RISC-V 101
/* common instructions */
mv a0, sp // a0 = sp
lw a0, (a1) // a0 = *a1
sw a0, (a1) // *a1 = a0
addi a0, a1, 111 // a0 = a1 + 111
bnez a0, <label> // branch if a0 is not equal to zero
jal ra, <label> // label 호출 후 return address를 ra에 저장
ret // ra 위치로 이동
/* privileged instructions */
csrr rd, csr // csr 레지스터 읽어오는 명령
csrw csr, rs
csrrw rd, csr, rs // 읽으면서 쓰는 명령
sret // trap handler에서 돌아오는 명령어
sfence.vma // TLB 비우는 명령어
- Modes
- U-mode : user 모드
- S-mode : kernel 모드
- M-mode : BIOS가 돌아가는 모드
- CSR (control and status register) : CPU settings이 저장된 레지스터
__asm__ __volatile__("assembly" : output_operands : input_operands : clobbered_registers); __asm__ __volatile__("csrw sscratch, %0" : : "r"(123)); - operands는 constraints 형태로 저장되는데 “r”(input)이랑 “=r”(output)이 있음
- clobbered_registers는 컴파일러한테 이 명령에 대한 어떤 정보를 알려주는 거임
Boot
- SBI (supervisor binary interface) : BIOS 같은 건데 kernel을 위한 API 같은 느낌임.
- OpenSBI는 QEMU에서 사용되는데 마지막에 0x80200000으로 점프함
- Linker script : linker가 참조하는 파일인데 여러 메모리 영역들을 어떤 주소에 할당할지 정해줄 수 있음
ENTRY(boot)
SECTIONS {
. = 0x80200000;
.text : {
KEEP(*(.text.boot));
*(.text .text.*);
}
.rodata : ALIGN(4) {
*(.rodata .rodata.*);
}
.data : ALIGN(4) {
*(.data .data.*);
}
.bss : ALIGN(4) {
__bss = .;
*(.bss .bss.* .sbss .sbss.*);
__bss_end = .;
}
. = ALIGN(4);
. += 1024 * 128;
__stack_top = .;
}
- kernel source
extern char *__bss, *__bss_end, *__stack_top;
void memset(unsigned char *begin, char c, unsigned int n)
{
for(unsigned int i=0;i<n;i++) {
*(begin + i) = c;
}
}
void main(void)
{
memset((unsigned *)__bss, 0, (__bss_end - __bss));
while(1);
}
__attribute__((section(".text.boot")))
__attribute__((naked))
void boot(void)
{
__asm__ __volatile__(
"mv sp, %0\n"
"j main\n"
:
: "r"(__stack_top)
);
}
printf 구현
struct sbiret {
long error;
long value;
};
struct sbiret sbi_call(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long fid, long eid)
{
register long a0 __asm__("a0") = arg0;
register long a1 __asm__("a1") = arg1;
register long a2 __asm__("a2") = arg2;
register long a3 __asm__("a3") = arg3;
register long a4 __asm__("a4") = arg4;
register long a5 __asm__("a5") = arg5;
register long a6 __asm__("a6") = fid;
register long a7 __asm__("a7") = eid;
__asm__ __volatile__("ecall"
: "=r"(a0), "=r"(a1)
: "r"(a0), "r"(a1), "r"(a2), "r"(a3), "r"(a4), "r"(a5), "r"(a6), "r"(a7)
: "memory");
return (struct retsbi) {.error = a0, .value = a1};
}
void putchar(char ch)
{
sbi_call(ch, 0, 0, 0, 0, 0, 0, 1);
}
- SBI가 제공하는 기능을 호출하기 위해서
ecall을 사용하면 됨ecall실행 시 M-mode로 전환되고 OpenSBI의 processing handler가 호출됨a0-a5는 인자로 사용됨a7은 EID(Extension ID)를 나타냄a6은 EID에 대한 FID(Function ID)를 나타냄- OpenSBI의 모든 기능의 callee는
a0(error)랑a1(return value)을 제외한 다른 레지스터는 내용을 바꾸지 않음
panic 구현
#define PANIC(fmt, ...) \
do { \
printf("PANIC: %s: %d: " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__); \
while (1) {} \
} while(0)
Exception 처리 구현
#define WRITE_CSR(register, value) \
do { \
uint32_t tmp = value; \
__asm__ __volatile__("csrw " #register ", %0" :: "r"(tmp)); \
} while(0)
WRITE_CSR(stvec, (uint32_t) kernel_entry);
- stvec을 통해 exception이 발생했을 때 호출할 함수를 등록할 수 있다
- stvec의 하위 2bit는 mode에 대한 정보가 저장되기 때문에 4바이트 단위로 align을 해줘야 됨
__attribute__((naked))
__attribute__((aligned(4)))
void kernel_entry(void) {
__asm__ __volatile__(
"csrw sscratch, sp\n"
"addi sp, sp, -4 * 31\n"
"sw ra, 4 * 0(sp)\n"
"sw gp, 4 * 1(sp)\n"
"sw tp, 4 * 2(sp)\n"
"sw t0, 4 * 3(sp)\n"
"sw t1, 4 * 4(sp)\n"
"sw t2, 4 * 5(sp)\n"
"sw t3, 4 * 6(sp)\n"
"sw t4, 4 * 7(sp)\n"
"sw t5, 4 * 8(sp)\n"
"sw t6, 4 * 9(sp)\n"
"sw a0, 4 * 10(sp)\n"
"sw a1, 4 * 11(sp)\n"
"sw a2, 4 * 12(sp)\n"
"sw a3, 4 * 13(sp)\n"
"sw a4, 4 * 14(sp)\n"
"sw a5, 4 * 15(sp)\n"
"sw a6, 4 * 16(sp)\n"
"sw a7, 4 * 17(sp)\n"
"sw s0, 4 * 18(sp)\n"
"sw s1, 4 * 19(sp)\n"
"sw s2, 4 * 20(sp)\n"
"sw s3, 4 * 21(sp)\n"
"sw s4, 4 * 22(sp)\n"
"sw s5, 4 * 23(sp)\n"
"sw s6, 4 * 24(sp)\n"
"sw s7, 4 * 25(sp)\n"
"sw s8, 4 * 26(sp)\n"
"sw s9, 4 * 27(sp)\n"
"sw s10, 4 * 28(sp)\n"
"sw s11, 4 * 29(sp)\n"
"csrr a0, sscratch\n"
"sw a0, 4 * 30(sp)\n"
"mv a0, sp\n"
"call handle_trap\n"
"lw ra, 4 * 0(sp)\n"
"lw gp, 4 * 1(sp)\n"
"lw tp, 4 * 2(sp)\n"
"lw t0, 4 * 3(sp)\n"
"lw t1, 4 * 4(sp)\n"
"lw t2, 4 * 5(sp)\n"
"lw t3, 4 * 6(sp)\n"
"lw t4, 4 * 7(sp)\n"
"lw t5, 4 * 8(sp)\n"
"lw t6, 4 * 9(sp)\n"
"lw a0, 4 * 10(sp)\n"
"lw a1, 4 * 11(sp)\n"
"lw a2, 4 * 12(sp)\n"
"lw a3, 4 * 13(sp)\n"
"lw a4, 4 * 14(sp)\n"
"lw a5, 4 * 15(sp)\n"
"lw a6, 4 * 16(sp)\n"
"lw a7, 4 * 17(sp)\n"
"lw s0, 4 * 18(sp)\n"
"lw s1, 4 * 19(sp)\n"
"lw s2, 4 * 20(sp)\n"
"lw s3, 4 * 21(sp)\n"
"lw s4, 4 * 22(sp)\n"
"lw s5, 4 * 23(sp)\n"
"lw s6, 4 * 24(sp)\n"
"lw s7, 4 * 25(sp)\n"
"lw s8, 4 * 26(sp)\n"
"lw s9, 4 * 27(sp)\n"
"lw s10, 4 * 28(sp)\n"
"lw s11, 4 * 29(sp)\n"
"lw sp, 4 * 30(sp)\n"
"sret\n"
);
}
- kernel entry에서는 registers를 stack에 저장해놓고 handle_trap을 호출
#define READ_CSR(register) \
({ \
uint32_t tmp; \
__asm__ __volatile__( \
"csrr %0, " #register : "=r"(tmp)); \
); \
tmp; \
})
struct trap_frame {
uint32_t ra;
uint32_t gp;
uint32_t tp;
uint32_t t0;
uint32_t t1;
uint32_t t2;
uint32_t t3;
uint32_t t4;
uint32_t t5;
uint32_t t6;
uint32_t a0;
uint32_t a1;
uint32_t a2;
uint32_t a3;
uint32_t a4;
uint32_t a5;
uint32_t a6;
uint32_t a7;
uint32_t s0;
uint32_t s1;
uint32_t s2;
uint32_t s3;
uint32_t s4;
uint32_t s5;
uint32_t s6;
uint32_t s7;
uint32_t s8;
uint32_t s9;
uint32_t s10;
uint32_t s11;
uint32_t sp;
} __attribute__((packed));
void handle_trap(struct trap_frame *f) {
uint32_t scause = READ_CSR(scause);
uint32_t stval = READ_CSR(stval);
uint32_t sepc = READ_CSR(sepc);
PANIC("unexpected trap scause=%x, stval=%x, sepc=%x\n", scause, stval, sepc);
}
- exception 정보에 대한 CSR
- scause : exception의 종류
- stval : exception 종류에 따른 추가 정보
- sepc : exception이 발생한 pc 위치
Memory allocation
{
. = ALIGN(4096);
__free_ram = .;
. += 4 * 1024 * 1024
__free_ram_end = .;
}
- linker script에 dynamic allocation을 위한 영역을 정의
#define PAGE_SIZE 4096
extern char __free_ram[], __free_ram_end[];
paddr_t allocate_pages(size_t n)
{
static paddr_t next_paddr = __free_ram;
paddr_t r = next_paddr;
next_paddr += n * PAGE_SIZE;
if (next_paddr > __free_ram_end)
PANIC("out of memory!");
return r;
}
- 단순한 linear allocation을 사용한 코드
Process